package com.franklin.java.completion;

import com.franklin.java.api.psi.data.PsiCallingElement;
import com.franklin.java.api.psi.data.PsiData;
import com.franklin.java.api.psi.data.resolver.PsiDataResolverFactory;
import com.franklin.java.utils.PsiUtils;
import com.franklin.plugins.common.collection.PrettyArrayList;
import com.franklin.plugins.common.collection.PrettyHashSet;
import com.franklin.plugins.common.utils.DataCheckUtils;
import com.franklin.plugins.common.utils.StringHelper;
import com.intellij.codeInsight.completion.CompletionContributor;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import org.apache.commons.lang3.ArrayUtils;
import org.jetbrains.annotations.NotNull;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * @author Franklin
 * @since 2021/8/5 11:59
 */
public abstract class AbstractJavaDataCompletionContributor extends CompletionContributor implements JavaCompletionContributor {

    public final static String VARIABLE_PATTERN = "^\\s*(?<paramName>[A-Za-z_]*\\w*)\\s*$";

    public final static String WHITE_SPACE_PARAM = "#WHITE#";

    protected final static ThreadLocal<String> callingElementType = new ThreadLocal<>();
    protected final static ThreadLocal<PsiCallingElement> callingElementCache = new ThreadLocal<>();

    protected final static ThreadLocal<Project> currentProject = new ThreadLocal<>();

    protected final static ThreadLocal<List<LookupElement>> getSetMethodElements = new ThreadLocal<>();
    protected final static ThreadLocal<List<LookupElement>> franklinElements = new ThreadLocal<>();
    private final static ThreadLocal<Set<String>> franklinHandledElements = new ThreadLocal<>();


    protected PsiElement findInputAfterDot(CompletionParameters parameters) {
        PsiElement psiElement = PsiUtils.findFirstNotWhitespaceElementFromLeft(parameters);
        if (PsiUtils.isJavaDot(psiElement)) {
            return null;
        }
        return psiElement;
    }

    public void init(CompletionParameters parameters){
        findCallingElement(parameters);
        setCurrentProject(parameters);
        findCallingElementType(parameters);
    }

    public void release(){
        releaseCallingElement();
        releaseCurrentProject();
        releaseCallingType();
    }

    protected void findCallingElement(CompletionParameters parameters){
        PsiCallingElement callingElement = PsiUtils.findCallingElement(parameters);
        if (Objects.isNull(callingElement)){
            return;
        }
        callingElementCache.set(callingElement);
    }

    protected void releaseCallingElement(){
        callingElementCache.remove();
    }

    protected void setCurrentProject(CompletionParameters parameters){
        currentProject.set(parameters.getEditor().getProject());
    }

    protected void releaseCurrentProject(){
        currentProject.remove();
    }

    @Override
    public boolean isCorrectCompletionParameter(@NotNull CompletionParameters parameters) {
        PsiFile psiFile = parameters.getOriginalFile();
        if (!psiFile.getName().endsWith(".java")) {
            return false;
        }
        String psiType = callingElementType.get();
        if (DataCheckUtils.isNotEmpty(psiType)) {
            return true;
        }

        return findCallingElementType(parameters);
    }

    protected boolean findCallingElementType(CompletionParameters parameters){
        PsiCallingElement psiCallingElement = PsiUtils.findCallingElement(parameters);
        if (Objects.isNull(psiCallingElement)) {
            return false;
        }
        PsiElement callingElement = psiCallingElement.getCallingElement();
        //判断是不是类名
        String callingElementText = callingElement.getText();
        PsiClass[] classes = PsiUtils.findClasses(parameters.getPosition().getProject(), callingElementText);
        if (ArrayUtils.isNotEmpty(classes)) {
            return false;
        }

        PsiData psiData = PsiDataResolverFactory.resolveData(callingElementText, callingElementText, callingElement);
        if (DataCheckUtils.isNotEmpty(psiData.getTypeFullName())) {
            callingElementType.set(psiData.getTypeFullName());
            return true;
        }
        return false;
    }

    protected void releaseCallingType(){
        callingElementType.remove();
    }

    protected String fillWhiteSpace(String fillExpression, String whiteSpace) {
        return fillExpression.replaceAll(WHITE_SPACE_PARAM, whiteSpace);
    }

    public static Project getCurrentProject() {
        return currentProject.get();
    }

    protected Set<String> getFranklinHandledElements() {
        Set<String> strings = franklinHandledElements.get();
        return DataCheckUtils.isEmpty(strings) ? Collections.emptySet() : strings;
    }

    /**
     * 添加处理的方法名<br>
     * @author Franklin
     * @since 2021/9/6 12:21
     * @param lookupElement:
     * @return void
     */
    protected void addHandledElement(LookupElement lookupElement) {
        Set<String> franklinHandledElementSet = getFranklinHandledElements();
        if (DataCheckUtils.isEmpty(franklinHandledElementSet)) {
            franklinHandledElementSet = new PrettyHashSet<>();
            franklinHandledElements.set(franklinHandledElementSet);
        }
        franklinHandledElementSet.add(lookupElement.getLookupString());
    }

    /**
     * 添加get set方法<br>
     * @author Franklin
     * @since 2021/9/6 12:21
     * @param lookupElement:
     * @return boolean
     */
    protected boolean addGetSetMethodElement(LookupElement lookupElement) {
        String lookupString = lookupElement.getLookupString();
        if (StringHelper.isGetSetMethod(lookupString)) {
            List<LookupElement> lookupElements = getSetMethodElements.get();
            if (DataCheckUtils.isEmpty(lookupElements)) {
                lookupElements = new PrettyArrayList<>();
                getSetMethodElements.set(lookupElements);
            }
            lookupElements.add(lookupElement);
            return true;
        }
        return false;
    }

    /**
     * 添加待处理的方法<br>
     * @author Franklin
     * @since 2021/9/6 12:22
     * @param lookupElement:
     * @return boolean
     */
    protected boolean addFranklinElement(LookupElement lookupElement) {
        List<LookupElement> lookupElements = franklinElements.get();
        if (DataCheckUtils.isEmpty(lookupElements)) {
            lookupElements = new PrettyArrayList<>();
            franklinElements.set(lookupElements);
        }
        addGetSetMethodElement(lookupElement);
        return lookupElements.add(lookupElement);
    }

    protected void releaseFranklinHandleElements() {
        franklinHandledElements.remove();
        getSetMethodElements.remove();
        franklinElements.remove();
    }

}
